package com.seadragon.app.ksr.service.impl;

import static com.seadragon.app.ksr.model.KsrConstants.GOOGLE_ADDRESS_COMPONENT_TYPE_CITY;
import static com.seadragon.app.ksr.model.KsrConstants.GOOGLE_ADDRESS_COMPONENT_TYPE_COUNTY;
import static com.seadragon.app.ksr.model.KsrConstants.GOOGLE_ADDRESS_COMPONENT_TYPE_NEIGHBORHOOD;
import static com.seadragon.app.ksr.model.KsrConstants.GOOGLE_ADDRESS_COMPONENT_TYPE_POSTAL_CODE;
import static com.seadragon.app.ksr.model.KsrConstants.GOOGLE_ADDRESS_COMPONENT_TYPE_ROUTE;
import static com.seadragon.app.ksr.model.KsrConstants.GOOGLE_ADDRESS_COMPONENT_TYPE_STATE;
import static com.seadragon.app.ksr.model.KsrConstants.GOOGLE_ADDRESS_COMPONENT_TYPE_STREET_NUMBER;
import static com.seadragon.app.ksr.model.KsrConstants.gf;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.geocoder.Geocoder;
import com.google.code.geocoder.GeocoderRequestBuilder;
import com.google.code.geocoder.model.GeocodeResponse;
import com.google.code.geocoder.model.GeocoderAddressComponent;
import com.google.code.geocoder.model.GeocoderGeometry;
import com.google.code.geocoder.model.GeocoderRequest;
import com.google.code.geocoder.model.GeocoderResult;
import com.google.code.geocoder.model.GeocoderStatus;
import com.google.code.geocoder.model.LatLng;
import com.seadragon.app.ksr.model.Address;
import com.seadragon.app.ksr.model.AddressType;
import com.seadragon.app.ksr.model.GeocodedAddress;
import com.seadragon.app.ksr.service.GeocodeService;
import com.seadragon.app.ksr.util.StringUtils;
import com.vividsolutions.jts.geom.Coordinate;


public class GeocodeServiceGoogleImpl implements GeocodeService {
	private static Logger logger = LoggerFactory.getLogger(GeocodeServiceGoogleImpl.class);
	private static Geocoder geocoder = new Geocoder();

	@Override
	public GeocodedAddress geocode(Address address) {
		GeocodedAddress geocodedAddress = geocode(address.toRawString());
		if(AddressType.ROUTE == geocodedAddress.getAddressType()){
			address.setNeighborhood(geocodedAddress.getNeighborhood());
			address.setGeocodedZip(geocodedAddress.getZip());
			address.setGeocodedCity(geocodedAddress.getCity());
			address.setGeocodedCounty(geocodedAddress.getCounty());
			address.setGeocodedState(geocodedAddress.getState());
			address.setStreetNumber(geocodedAddress.getStreetNumber());
			address.setStreetName(geocodedAddress.getStreetName());
			address.setStreetType(geocodedAddress.getStreetType());
		}else if(AddressType.NEIGHBORHOOD == geocodedAddress.getAddressType()){
			address.setNeighborhood(geocodedAddress.getNeighborhood());
			address.setGeocodedZip(geocodedAddress.getZip());
			address.setGeocodedCity(geocodedAddress.getCity());
			address.setGeocodedCounty(geocodedAddress.getCounty());
			address.setGeocodedState(geocodedAddress.getState());
		}else if(AddressType.ZIP == geocodedAddress.getAddressType()){
			address.setGeocodedZip(geocodedAddress.getZip());
			address.setGeocodedCity(geocodedAddress.getCity());
			address.setGeocodedCounty(geocodedAddress.getCounty());
			address.setGeocodedState(geocodedAddress.getState());
		}else if(AddressType.CITY == geocodedAddress.getAddressType()){
			address.setGeocodedCity(geocodedAddress.getCity());
			address.setGeocodedCounty(geocodedAddress.getCounty());
			address.setGeocodedState(geocodedAddress.getState());
		}else if(AddressType.COUNTY == geocodedAddress.getAddressType()){
			address.setGeocodedCounty(geocodedAddress.getCounty());
			address.setGeocodedState(geocodedAddress.getState());
		}else if(AddressType.STATE == geocodedAddress.getAddressType()){
			address.setGeocodedState(geocodedAddress.getState());
		}
		address.setLocation(geocodedAddress.getPos());
		return geocodedAddress;
	}

	@Override
	public GeocodedAddress geocode(String addressString) {
		GeocodedAddress geocodedAddress = null;
		GeocoderRequest request = new GeocoderRequestBuilder().setAddress(addressString).setLanguage("en").getGeocoderRequest();
		GeocodeResponse response = geocoder.geocode(request);
		if (response.getStatus() == GeocoderStatus.OK) {
			List<GeocoderResult> results = response.getResults();
			for (GeocoderResult result : results) {
				GeocodedAddress ga = new GeocodedAddress();
				GeocoderGeometry geometry = result.getGeometry();
				LatLng latLng = geometry.getLocation();
				Coordinate coor = new Coordinate(latLng.getLng().doubleValue(), latLng.getLat().doubleValue());
				ga.setPos(gf.createPoint(coor));
				List<GeocoderAddressComponent> components = result.getAddressComponents();
				for(GeocoderAddressComponent component : components){
					List<String> types = component.getTypes();
					for(String type : types){
						if(type.equalsIgnoreCase(GOOGLE_ADDRESS_COMPONENT_TYPE_COUNTY)){
							if(ga.getAddressType() == null){
								ga.setAddressType(AddressType.COUNTY);
							}else if(ga.getAddressType().ordinal() > AddressType.COUNTY.ordinal()){
								ga.setAddressType(AddressType.COUNTY);
							}
							ga.setCounty(component.getLongName());
						}else if(type.equalsIgnoreCase(GOOGLE_ADDRESS_COMPONENT_TYPE_NEIGHBORHOOD)){
							if(ga.getAddressType() == null){
								ga.setAddressType(AddressType.NEIGHBORHOOD);
							}else if(ga.getAddressType().ordinal() > AddressType.NEIGHBORHOOD.ordinal()){
								ga.setAddressType(AddressType.NEIGHBORHOOD);
							}
							ga.setNeighborhood(component.getLongName());
						}else if(type.equalsIgnoreCase(GOOGLE_ADDRESS_COMPONENT_TYPE_STREET_NUMBER)){
							if(ga.getAddressType() == null){
								ga.setAddressType(AddressType.ROUTE);
							}else if(ga.getAddressType().ordinal() > AddressType.ROUTE.ordinal()){
								ga.setAddressType(AddressType.ROUTE);
							}
							ga.setStreetNumber(component.getShortName());
						}else if(type.equalsIgnoreCase(GOOGLE_ADDRESS_COMPONENT_TYPE_ROUTE)){
							if(ga.getAddressType() == null){
								ga.setAddressType(AddressType.ROUTE);
							}else if(ga.getAddressType().ordinal() > AddressType.ROUTE.ordinal()){
								ga.setAddressType(AddressType.ROUTE);
							}
							ga.setStreetName(component.getShortName());
						}else if(type.equalsIgnoreCase(GOOGLE_ADDRESS_COMPONENT_TYPE_CITY)){
							if(ga.getAddressType() == null){
								ga.setAddressType(AddressType.CITY);
							}else if(ga.getAddressType().ordinal() > AddressType.CITY.ordinal()){
								ga.setAddressType(AddressType.CITY);
							}
							ga.setCity(component.getShortName());
						}else if(type.equalsIgnoreCase(GOOGLE_ADDRESS_COMPONENT_TYPE_STATE)){
							if(ga.getAddressType() == null){
								ga.setAddressType(AddressType.STATE);
							}
							ga.setState(component.getShortName());
						}else if(type.equalsIgnoreCase(GOOGLE_ADDRESS_COMPONENT_TYPE_POSTAL_CODE)){
							if(!StringUtils.isZip(addressString)) continue;
							if(ga.getAddressType() == null){
								ga.setAddressType(AddressType.ZIP);
							}else if(ga.getAddressType().ordinal() > AddressType.ZIP.ordinal()){
								ga.setAddressType(AddressType.ZIP);
							}
							ga.setZip(component.getShortName());
						}
					}
				}
				logger.info(result.toString());
				geocodedAddress = ga;
				break;
			}
			logger.info(response.toString());
		}

		return geocodedAddress;
	}

	@Override
	public GeocodedAddress normalize(String addressString) {
		return null;
	}
}
